package com.fjb.tool.bio.block;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;

/**
 * @Description:多人聊天 bio多线程服务端 
 * @author hemiao
 * @time:2020年4月17日 下午2:08:27
 */
public class ImThreadBioServer {
	
	/**
	 * 默认端口
	 */
	private int DEFAULT_PROT = 8088;
	
	/**
	 * 退出
	 */
	private String QUIT = "quit";
	
	/**
	 * serverSocket
	 */
	private ServerSocket serverSocket;
	
	/**
	 * 线程池
	 */
	private ExecutorService executorService;
	
	/**
	 * 用户容器map
	 */
	private Map<Integer, Writer> userContainerMap;
	
	/**
	 * 指定端口
	 * @param port
	 * @param nThreads
	 */
	public ImThreadBioServer(Integer port,int nThreads) {
		
		// 创建用户容器 	
		userContainerMap = new HashMap<Integer, Writer>();
		//System.out.println(" 服务端  用户容器初始化......");
		
		if(port!=null) {
			DEFAULT_PROT = port;
		}
		// 端口初始化
		//System.out.println(" 服务端  端口初始化 port = " + DEFAULT_PROT);
		
		// 线程池
	    //executorService = Executors.newFixedThreadPool(nThreads);
		//System.out.println(" 服务端  线程池创建成功  nThreads = "+nThreads);
	}
	
	/**
	 * @Description:启动服务
	 * @param serverSocket
	 * void
	 * @exception:
	 * @author: hemiao
	 * @time:2020年4月17日 下午2:52:09
	 */
	public void start() {
		try {	
			serverSocket = new ServerSocket(DEFAULT_PROT);
			System.out.println(" 服务端  启动成功  port = "+DEFAULT_PROT);
		} catch (IOException e1) {
			e1.printStackTrace();
			System.out.println(" 服务端启动失败...... ");
		}
		try {	
			while (true) {
				// 等待客户连接   serverSocket.accept 是一个阻塞函数  一直等待用户连接   有用户连接才会执行  下面一行代码
				System.out.println("等待客户连接...... ");
				Socket socket = serverSocket.accept();	
				// 客户连接端口号
				int port = socket.getPort();
				// 当前线程的名称
				String currentThreadName = Thread.currentThread().getName();
				System.out.println("客户 连接   port =  "+port+" , 当前线程名称 ："+currentThreadName);
				// 开启一个线程处理用户
				new Thread(()->{
					try {
						// 添加用户
						addUser(socket);
						
						// 获得 字节输入流
						InputStream inputStream = socket.getInputStream();
						
						// 获得 字节输出流
						OutputStream outputStream = socket.getOutputStream();
						// 获得用户信息	
						BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
						// 是关闭
						System.out.println("isClosed 是关闭  = "+socket.isClosed());
						// 是连接
						System.out.println("isConnected 是连接 = "+socket.isConnected());
						
						String readLine = null;
						// reader.readLine()  readLine()是一个阻塞函数，当没有数据读取时，就一直会阻塞在那，而不是返回null；一个办法是发送完数据后就关掉流，这样readLine()结束阻塞状态
			            while ((readLine = reader.readLine()) != null) {
							String fwdMsg = "客户端[" + socket.getPort() + "]: " + readLine + "\n";
							System.out.println(fwdMsg);
							
							// 将消息发送给其它用户
							forwardMsg(socket, fwdMsg);
							
							// 判断是否是 退出
							if(readLine.equals(QUIT)) {
								break;
							}
						}
			            System.out.println("服务端   --> readLine()是一个阻塞函数  关掉流 才会执行这个 ");
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}finally{
						try {	
							removeUser(socket);
						} catch (IOException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
						
				},currentThreadName).start();
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * @Description:关闭服务
	 * @param serverSocket
	 * void
	 * @exception:
	 * @author: hemiao
	 * @time:2020年4月17日 下午2:21:13
	 */
	public void colse(ServerSocket serverSocket) {
		if(serverSocket!=null) {
			try {
				serverSocket.close();
				System.out.println(" 服务端  bio 多线程   关闭 ");
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * @Description: 用户添加
	 * @param socket
	 * void
	 * @exception:
	 * @author: hemiao
	 * @throws IOException 
	 * @time:2020年4月17日 下午3:40:38
	 */
	public void addUser(Socket socket) throws IOException {
		if(socket!=null) {
			// 用户端口唯一 	
			int port = socket.getPort();
			OutputStream outputStream = socket.getOutputStream();
			OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
			BufferedWriter writer = new BufferedWriter(outputStreamWriter);
			userContainerMap.put(port, writer);
			System.out.println("客户端  [ " + port + " ] 已连接到服务器");
		}
	}
	
	/**
	 * @Description:用户删除
	 * @param socket
	 * void
	 * @exception:
	 * @author: hemiao
	 * @throws IOException 
	 * @time:2020年4月17日 下午3:40:47
	 */
	public void removeUser(Socket socket) throws IOException {
		if(socket!=null) {
			// 用户端口唯一 
			int port = socket.getPort();
			if(userContainerMap.containsKey(port)) {
				userContainerMap.get(port).close();
				// 删除用户
				userContainerMap.remove(port);
	            System.out.println("客户端[" + port + "]已断开连接");
			}
		}
	}
	
	/**
	 * @Description:转发信息 除了自己
	 * @param socket
	 * @param msg
	 * @throws IOException
	 * void	
	 * @exception:
	 * @author: hemiao
	 * @time:2020年4月17日 下午10:03:52
	 */
	public void forwardMsg(Socket socket,String msg) throws IOException {
		if(socket!=null) {
			int port = socket.getPort();
			for (Integer id : userContainerMap.keySet()) {
				if(!id.equals(port)) {
					Writer writer = userContainerMap.get(id);
					writer.write(msg);
					writer.flush();
				}
			}
		}
	}
	
	
	public static void main(String[] args) {
		ImThreadBioServer imThreadBioServer = new ImThreadBioServer(8088,3);
		imThreadBioServer.start();
	}
}
